#include "tss.h"
#include "debug.h"
#include "print.h"
#include "string.h"
#include "global.h"

struct tss{
    uint32_t backlink;
    uint32_t* esp0;
    uint32_t ss0;
    uint32_t* esp1;
    uint32_t ss1;
    uint32_t* esp2;
    uint32_t ss2;
    uint32_t cr3;
    uint32_t (*eip) (void);
    uint32_t eflags;
    uint32_t eax;
    uint32_t ecx;
    uint32_t edx;
    uint32_t ebx;
    uint32_t esp;
    uint32_t ebp;
    uint32_t esi;
    uint32_t edi;
    uint32_t es;
    uint32_t cs;
    uint32_t ss;
    uint32_t ds;
    uint32_t fs;
    uint32_t gs;
    uint32_t ldt;
    uint32_t trace;
    uint32_t io_base;
};

static struct tss tss;

/* 创建gdt描述符 */
static struct gdt_desc _make_gdt_desc(uint32_t* desc_addr, uint32_t limit, uint8_t attr_low, uint8_t attr_high) {
   uint32_t desc_base = (uint32_t)desc_addr;
   struct gdt_desc desc;

   desc.base_low_word = (uint16_t)(desc_base & 0x0000ffff);
   desc.base_mid_byte = (uint8_t)((desc_base & 0x00ff0000) >> 16);
   desc.base_high_byte = (uint8_t)((desc_base & 0xff000000) >> 24);

   desc.attr_low_byte = (uint8_t)(attr_low);

   desc.limit_low_word = (uint16_t)(limit & 0x0000ffff);
   
   desc.limit_high_attr_high = (uint8_t)((uint8_t)(attr_high << 4) + 
                                         (uint8_t)((uint8_t)(limit & 0x000f0000 >> 16)));

   return desc;
}

void update_tss_esp(struct task_struct* pthread){
    tss.esp0 = (uint32_t*)((uint32_t)pthread->self_kstack + PG_SIZE);
}

void tss_init(void){
    put_str("   tss_init start\n");
    memset(&tss, 0, sizeof(tss));
    /* io位图的偏移地址大于等于tss大小则认为没有io位图 */
    tss.io_base = sizeof(tss);
    tss.ss0 = SELECTOR_K_STACK;

    /* 制作tss的描述符并存储tss描述符所在的位置 即第4个 */
    *(struct gdt_desc*)(0xc0000920) = _make_gdt_desc((uint32_t*)&tss, sizeof(tss), TSS_ATTR_LOW, TSS_ATTR_HIGH);
    *(struct gdt_desc*)(0xc0000928) = _make_gdt_desc((uint32_t*)0, 0xfffff, GDT_ATTR_HIGH, GDT_CODE_ATTR_LOW_DPL3);
    *(struct gdt_desc*)(0xc0000930) = _make_gdt_desc((uint32_t*)0, 0xfffff, GDT_ATTR_HIGH, GDT_DATA_ATTR_LOW_DPL3);
    /* 这里应该加载gdt描述符表地址到gdtr */
    uint64_t _gdtr = ((8 * 7 - 1)) | (uint64_t)((uint32_t)(0xc0000900) << 16);
    asm volatile("lgdt %0" : : "m"(_gdtr) :);
    // /* 这里应该加载tss的选择子到tr */
    asm volatile("ltr %w0" : : "r"(SELECTOR_TSS) :);
    put_str("   tss_init done\n\n");
}